package com.ipan.jfinal.simpleApi;

import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ipan.jfinal.controller.ResultDefineManager;
import com.ipan.kits.mapper.FastJsonMapper;
import com.ipan.kits.net.IPUtil;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.kit.Kv;

/**
 * 简单的API接口通信拦截器
 * 
 * http头部
 * Authorization（可自定义）: OPEN-BODY-SIG AppId="AppId", UserId="UserId", Timestamp="时间戳", Nonce="随机数", Signature="签名"
 * Accept: application/json
 * Charset: UTF-8
 * 
 * http正文（Map格式的Json字符串）
 * {
 * 	"orgCusSendID": "xxxxxxxxxx", // 流水号（长度不超过32位）
 * 	"reqData": {...} // 请求参数
 * }
 * --encryption 加密方式（0-明文 1-SHA256加密 2-MD5加密 3 SM4加密）
 * 
 * http正文加密（Map格式的Json字符串）
 * {"data":"xxxxxxxxxxxxxxxxxxxxxxxxxxx"}
 * 解密后得到字符：
 * {
 * 	"orgCusSendID": "xxxxxxxxxx", // 流水号（长度不超过32位）
 * 	"reqData": {...} // 请求参数
 * }
 * 
 * 处理
 * 1）获取http头部、http正文；
 * 2）头部及正文的格式校验；
 * 3）校验签名是否合法；签名合法=身份校验+内容校验，校验的内容是指明文的json字符串；
 * 
 * @author iPan
 * @version 2022-06-29
 */
public class SimpleApiInterceptor implements Interceptor {

	private Logger log = LoggerFactory.getLogger(getClass());
	private AppkeyFetchable fetcher = null;
	
	public SimpleApiInterceptor(AppkeyFetchable fetcher) {
		this.fetcher = fetcher;
	}

	@Override
	public void intercept(Invocation inv) {
		SimpleApiController con = (SimpleApiController) inv.getController(); // API接口必须依赖SimpleApiController控制器
		String headKey = AuthHead.getKey(); // 部分平台的头部是全小写的
		String authHead = con.getHeader(headKey);
		if (StringUtils.isBlank(authHead)) { // 为空再取一次（小写）
			authHead = con.getHeader(headKey.toLowerCase());
		}
		String body = con.getRawData();
		try {
			log.info("authHead={}", authHead);
			log.info("body={}", StringUtils.substring(body, 0, 512));
			if (StringUtils.isBlank(authHead) || StringUtils.isBlank(body)) {
				errorRequestNoEncrypt(con, authHead, body, "非法请求"); // 没进入框架的请求，返回明文，不用密文返回；
				return ;
			}
			
			AuthHead authHeadObj = null;
			try {
				authHeadObj = AuthHead.parseHead(authHead);
			} catch (Exception e) {
				errorRequestNoEncrypt(con, authHead, body, "非法请求"); // 没进入框架的请求，返回明文，不用密文返回；
				return ;
			}
			String userId = authHeadObj.getUserId(); // 用户ID
			String appkey = fetcher.getAppkey(userId); // 用户APPKEY
			if (StringUtils.isBlank(appkey)) {
				errorRequestNoEncrypt(con, authHead, body, "用户不存在"); // 没进入框架的请求，返回明文，不用密文返回；
				return ;
			}
			
			con.setAttr("userId", userId);
			con.setAttr("appkey", appkey);
			Map<String, Object> map = FastJsonMapper.me().jsonToMap(body);
			String jsonText = body;
			String orgCusSendID = (String) map.get("orgCusSendID"); // 流水号
			Object reqData = map.get("reqData"); // 请求参数统一写在reqData里面（类型可能是Map、也可能是List<Map>）
			if (StringUtils.isBlank(orgCusSendID)) { // 自动识别加密传输
				String data = (String) map.get("data"); // 密文
				if (StringUtils.isBlank(data)) { // 非法请求
					errorRequest(con, authHead, body, "加密格式错误");
					return ;
				}
				jsonText = SimpleApiManager.getEncrypter().decrypt(data, appkey); // 解密正文
				map = FastJsonMapper.me().jsonToMap(jsonText);
				orgCusSendID = (String) map.get("orgCusSendID");
				reqData = map.get("reqData");
			}
			if (StringUtils.isBlank(orgCusSendID)) {
				errorRequest(con, authHead, body, "请求流水号不能为空");
				return ;
			}
			
	//		String encryption = (String) map.get("encryption"); // 暂没用到（指定加密方式）
			// 保存参数
			con.setAttr("orgCusSendID", orgCusSendID);
			con.setAttr("reqData", reqData);
			// 校验签名
			boolean isSuccess = SimpleApiManager.validation(fetcher, authHead, jsonText, appkey);
			if (!isSuccess) {
				errorRequest(con, authHead, body, "签名校验失败");
				return ;
			}
			log.info("API接口请求，userId={}，orgCusSendID={}。", userId, orgCusSendID);
			
			// 继续执行
			inv.invoke(); // 异常会执行全局异常处理器，默认返回500错误（根据html或json请求，自动识别）；
		} catch(Exception e) {
			log.error("API接口调用失败", e);
			errorRequest(con, authHead, body, "接口调用失败"); // 统一使用renderEncryptJson方法渲染，保证一致性；
		}
	}

	private void errorRequest(SimpleApiController con, String authHead, String body, String msg) {
		Kv param = Kv.by("code", ResultDefineManager.me().getFail().getLeft()).set("msg", msg);
//		con.renderEncryptJson(con.retFail(msg)); // 拦截器错误，不需要提示调用次数、调用总数；
		con.renderEncryptJson(param);
		String remoteIp = IPUtil.getIpAddr(con.getRequest());
		log.error("{}，host={}，authHead={}，body={}。", msg, remoteIp, authHead, body);
		
	}
	
	private void errorRequestNoEncrypt(SimpleApiController con, String authHead, String body, String msg) {
		Kv param = Kv.by("code", ResultDefineManager.me().getFail().getLeft()).set("msg", msg);
//		con.renderEncryptJson(con.retFail(msg)); // 拦截器错误，不需要提示调用次数、调用总数；
		con.renderJson(param);
		String remoteIp = IPUtil.getIpAddr(con.getRequest());
		log.error("{}，host={}，authHead={}，body={}。", msg, remoteIp, authHead, body);
	}
	
}
